home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
dev
/
devNet.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
23KB
|
820 lines
/*
* devNet.c --
*
* Device interface to the network. The routines here implement
* filesystem devices for the various ethernet protocols. Input
* queues of received packets are maintainted here, although the
* device has to be open before packets are queued. There is a
* different queue for each different ethernet protocol, and the
* device unit number is used to identify the protocol.
*
* TODO: this needs to be fixed to understand more than one network
* interface. It seems that at open time the correct interface
* should be chosen. Also, some interface-data needs to be passed
* down to the output routines so they can choose the right interface.
*
* Copyright 1987 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/dev/devNet.c,v 9.10 92/06/03 22:54:22 voelker Exp $ SPRITE (Berkeley)";
#endif not lint
#include <sprite.h>
#include <stdio.h>
#include <net.h>
#include <fs.h>
#include <vm.h>
#include <fsio.h>
#include <devNet.h>
#include <user/netInet.h>
#include <stdlib.h>
#include <sync.h>
#include <fsioDevice.h>
#include <user/net.h>
#include <bstring.h>
Boolean devNetDebug = FALSE;
/*
* The packets are kept in a circular queue whose length is a power of 2.
* Stuff gets added to the queues at interrupt time so removal requires
* synchronization with MASTER_LOCK/UNLOCK.
*/
#define PACKET_QUEUE_LEN 16
typedef struct {
int head;
int tail;
Address packet[PACKET_QUEUE_LEN];
int size[PACKET_QUEUE_LEN];
} PacketQueue;
/*
* A circular queue is full if its tail is right in front of its
* head (mod length). A queue is empty if its tail is its head.
*/
#define NextTail(queue) (((queue).tail + 1) & (PACKET_QUEUE_LEN -1))
#define NextHead(queue) (((queue).head + 1) & (PACKET_QUEUE_LEN -1))
#define QueueFull(queue) (NextTail(queue) == (queue).head)
#define QueueEmpty(queue) ((queue).tail == (queue).head)
/*
* Event counters kept on a per-device basis.
*/
typedef struct DeviceStats {
int shorts; /* Number of short packets received */
int drops; /* Number of packets dropped when queue full */
} DeviceStats;
/*
* State for the devices. They are linked together and scanned when
* a packet is received. Packets with protocols that don't match any
* of the protocols in this list will be dropped.
*/
typedef struct DeviceState {
List_Links links;
Net_Interface *interPtr; /* Interface associated with the
* device; */
int protocol; /* Protocol associated with device. */
Boolean kernel; /* TRUE if kernel is using this proto */
PacketQueue queue; /* Queue of received packets */
DeviceStats stats; /* Event counters */
Fs_NotifyToken fsReadyToken; /* Used for filesystem callback that
* notifies waiting processes that
* packets are here */
int (*inputProc)(); /* Used when the kernel is using the
* protocol. */
} DeviceState;
/*
* Queue of all devices associated with an interface.
*/
typedef struct DeviceQueueState {
List_Links links; /* Queue of DeviceState structures
* for this interface. */
Sync_Semaphore mutex; /* Synchronizes access to the queue. */
} DeviceQueueState;
extern void DevNetHandler();
static ReturnStatus ProtocolFromPacket();
static ReturnStatus ProtocolFromDevice();
static DeviceQueueState *QueueFromInterface();
/*
*----------------------------------------------------------------------
*
* DevNet_FsOpen --
*
* Open an ethernet protocol device. Protocol state is set up and
* linked in to a list of active protocols. The protocol is marked
* open to enable queueing of packets for this protocol.
*
* Results:
* SUCCESS - the operation was successful.
* FS_FILE_BUSY - the device was already opened.
*
* Side effects:
* Storage for the protocol state is allocated and linked into
* the list of active protocols.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevNet_FsOpen(devicePtr, useFlags, data, flagsPtr)
Fs_Device *devicePtr; /* Device info, unit number == protocol */
int useFlags; /* Flags from the stream being opened */
Fs_NotifyToken data; /* Call-back for input notification */
int *flagsPtr; /* OUT: Device flags. */
{
register int i;
unsigned int protocol;
ReturnStatus status = SUCCESS;
DeviceState *statePtr;
DeviceQueueState *deviceQueuePtr;
Boolean newFormat = FALSE;
Net_NetworkType netType;
int interface;
int unitProto = 0;
Net_Interface *interPtr;
DeviceState *itemPtr;
int maxSize;
if (devNetDebug) {
printf("DevNet_FsOpen: opening device 0x%x 0x%x\n",
devicePtr->type, devicePtr->unit);
}
statePtr = (DeviceState *) (devicePtr->data);
if (statePtr == (DeviceState *) NIL) {
statePtr = (DeviceState *) malloc(sizeof(DeviceState));
bzero((char *) statePtr, sizeof(DeviceState));
List_InitElement((List_Links *) statePtr);
} else {
/*
* If the device has a state structure then it must be in use.
*/
if (devNetDebug) {
printf("Extra open\n");
}
return FS_FILE_BUSY;
}
if (!(devicePtr->unit & DEV_NET_COMPAT_BIT)) {
interface = 0;
netType = NET_NETWORK_ETHER;
protocol = devicePtr->unit;
if (devNetDebug) {
printf("Device has old unit number\n");
}
} else {
int tmp = (devicePtr->unit & ~DEV_NET_COMPAT_BIT);
netType = DEV_NET_NETTYPE_FROM_UNIT(tmp);
interface = DEV_NET_NUMBER_FROM_UNIT(tmp);
unitProto = DEV_NET_PROTO_FROM_UNIT(tmp);
if (devNetDebug) {
printf("Device has new unit number\n");
}
newFormat = TRUE;
}
interPtr = Net_GetInterface(netType, interface);
if (interPtr == (Net_Interface *) NIL) {
if (devNetDebug) {
printf("No interface.\n");
}
free((char *) statePtr);
return DEV_NO_DEVICE;
}
statePtr->interPtr = interPtr;
if (newFormat) {
status = ProtocolFromDevice(unitProto, interPtr, &protocol);
if (status != SUCCESS) {
if (devNetDebug) {
printf("No such protocol %d\n", protocol);
}
free((char *) statePtr);
return DEV_NO_DEVICE;
}
}
deviceQueuePtr = QueueFromInterface(interPtr);
switch(netType) {
case NET_NETWORK_ETHER:
/*
* We keep the protocol number in network byte order so as to match
* the values coming off the net.
*/
protocol = (unsigned int)
Net_HostToNetShort((unsigned short) protocol);
break;
}
statePtr->protocol = protocol;
/*
* Pre-allocate buffer space for the input queue.
* This is probably a bad idea for anything but an ethernet since
* packet sizes may get large.
*/
switch(netType) {
case NET_NETWORK_ETHER:
maxSize = NET_ETHER_MAX_BYTES;
break;
case NET_NETWORK_ULTRA:
maxSize = NET_ULTRA_MAX_BYTES;
break;
case NET_NETWORK_FDDI:
maxSize = NET_FDDI_MAX_BYTES;
break;
default:
printf("DevNet_FsOpen: unknown net type %d\n", netType);
return DEV_NO_DEVICE;
}
for (i=0 ; i< PACKET_QUEUE_LEN ; i++) {
statePtr->queue.packet[i] = (Address) malloc (maxSize);
}
/*
* Differentiate between user-level reads and the kernel.
* DevNetEtherHandler will handle packets differently in
* the two cases. It notifies the user-level process, or
* it calls the kernel protocol handler.
*/
if (useFlags & FS_USER) {
statePtr->kernel = FALSE;
statePtr->fsReadyToken = data;
} else {
statePtr->kernel = TRUE;
#ifndef lint
statePtr->inputProc = (int(*)())data;
#endif /* lint */
}
statePtr->queue.head = 0;
statePtr->queue.tail = 0;
devicePtr->data = (ClientData) statePtr;
MASTER_LOCK(&deviceQueuePtr->mutex);
LIST_FORALL((List_Links *) deviceQueuePtr, (List_Links *) itemPtr) {
if (itemPtr->protocol == protocol) {
panic("DevNet_FsOpen: found protocol already in use.\n");
}
}
List_Insert((List_Links *)statePtr,
LIST_ATREAR((List_Links *) deviceQueuePtr));
Net_SetPacketHandler(interPtr, DevNetHandler);
MASTER_UNLOCK(&deviceQueuePtr->mutex);
if (devNetDebug) {
printf("DevNet_FsOpen: Open proto 0x%x status 0x%x\n",
protocol, status);
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* DevNet_FsReopen --
*
* Reopen an ethernet protocol device. Call the regular
* open routine to do all the work.
*
* Results:
* SUCCESS - the operation was successful.
* FS_FILE_BUSY - the device was already opened.
*
* Side effects:
* Storage for the protocol state is allocated and linked into
* the list of active protocols.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevNet_FsReopen(devicePtr, refs, writers, data, flagsPtr)
Fs_Device *devicePtr; /* Device info, unit number == protocol */
int refs; /* Number of open network streams */
int writers; /* Number that are open for writing */
Fs_NotifyToken data; /* Call-back for input notification */
int *flagsPtr; /* OUT: Device flags. */
{
int useFlags = FS_READ;
if (writers) {
useFlags |= FS_WRITE;
}
return( DevNet_FsOpen(devicePtr, useFlags, data, flagsPtr) );
}
/*
*----------------------------------------------------------------------
*
* DevNetHandler --
*
* Dispatcher for packets. The list of active protocols
* is scanned for a matching protocol. If found, the packet is
* enqueued for the protocol.
*
* Note: This routine is called from the network interrupt routine.
*
* Results:
* None.
*
* Side effects:
* The packet is saved in the protocols queue (if there's room and
* the protocol device is currently open).
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
void
DevNetHandler(interPtr, size, packetPtr)
Net_Interface *interPtr; /* Network interface. */
int size; /* Size of the packet. */
Address packetPtr; /* Pointer to the packet in the hardware
* receive buffer. */
{
register DeviceQueueState *deviceQueuePtr;
int protocol;
DeviceState *statePtr;
ReturnStatus status;
deviceQueuePtr = (DeviceQueueState *) interPtr->devNetData;
if (deviceQueuePtr == (DeviceQueueState *) NIL) {
return;
}
status = ProtocolFromPacket(packetPtr, interPtr->netType, &protocol);
if (status != SUCCESS) {
printf("DevNetHandler: couldn't get protocol from packet.\n");
return;
}
if (devNetDebug) {
printf("DevNetHandler %d:%d 0x%x %d\n", interPtr->netType,
interPtr->number, protocol, size);
}
MASTER_LOCK(&deviceQueuePtr->mutex);
LIST_FORALL((List_Links *) deviceQueuePtr, (List_Links *)statePtr) {
if (protocol == statePtr->protocol) {
if (QueueFull(statePtr->queue)) {
statePtr->stats.drops++;
} else {
bcopy(packetPtr,statePtr->queue.packet[statePtr->queue.tail],
size);
statePtr->queue.size[statePtr->queue.tail] = size;
statePtr->queue.tail = NextTail(statePtr->queue);
if (statePtr->kernel) {
/*
* Indirect to a process to pull the packet off of
* the input queue. We can't call the protocol handlers
* directly because they use malloc and free.
*/
Proc_CallFunc((void (*)())statePtr->inputProc,
(ClientData)statePtr, 0);
} else {
Fsio_DevNotifyReader(statePtr->fsReadyToken);
}
}
break;
}
}
MASTER_UNLOCK(&deviceQueuePtr->mutex);
}
/*
*----------------------------------------------------------------------
*
* DevNet_FsRead --
*
* Read a packet from a network device. This returns the first
* packet from the device's input queue, if any. This returns
* data from at most 1 network packet. If the caller's buffer is
* too short, the packet is truncated.
*
* Results:
* SUCCESS - if a packet was found in the queue.
* FS_WOULD_BLOCK - no packets found.
*
* Side effects:
* Removes the first packet from the queue and copies it into
* the receiver's buffer.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevNet_FsRead(devicePtr, readPtr, replyPtr)
Fs_Device *devicePtr;
Fs_IOParam *readPtr; /* Read parameter block */
Fs_IOReply *replyPtr; /* Return length and signal */
{
ReturnStatus status;
register DeviceState *statePtr;
register Address packetPtr;
register int size;
DeviceQueueState *deviceQueuePtr;
statePtr = (DeviceState *) devicePtr->data;
deviceQueuePtr = (DeviceQueueState *) statePtr->interPtr->devNetData;
MASTER_LOCK(&deviceQueuePtr->mutex);
if (QueueEmpty(statePtr->queue)) {
size = 0;
status = FS_WOULD_BLOCK;
if (devNetDebug) {
printf("DevNet_FsRead: empty queue, %d:%d proto 0x%x\n",
statePtr->interPtr->netType,
statePtr->interPtr->number,
statePtr->protocol);
}
} else {
packetPtr = statePtr->queue.packet[statePtr->queue.head];
size = statePtr->queue.size[statePtr->queue.head];
statePtr->queue.head = NextHead(statePtr->queue);
if (size > readPtr->length) {
size = readPtr->length;
}
bcopy(packetPtr, readPtr->buffer, size);
status = SUCCESS;
if (devNetDebug) {
printf("DevNet_FsRead: Found packet %d:%d proto 0x%x, size %d\n",
statePtr->interPtr->netType,
statePtr->interPtr->number,
statePtr->protocol, size);
}
}
replyPtr->length = size;
MASTER_UNLOCK(&deviceQueuePtr->mutex);
return(status);
}
/*
*----------------------------------------------------------------------
*
* DevNet_FsWrite --
*
* Pass a packet off to the network driver for output. The driver
* maintains a transmit queue so we don't have to. The protocol
* in the output packet header is verified to be the one corresponding
* to the device file.
*
* Results:
* SUCCESS - the packet was transmitted.
* SYS_INVALID_ARG - packet is too small or the protocol in the
* buffer doesn't match the one for the device.
*
* Side effects:
* Initiates transmission of the packet.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevNet_FsWrite(devicePtr, writePtr, replyPtr)
Fs_Device *devicePtr;
Fs_IOParam *writePtr; /* Standard write parameter block */
Fs_IOReply *replyPtr; /* Return length and signal */
{
register DeviceState *statePtr;
Net_ScatterGather ioVector;
Net_Interface *interPtr;
int dataSize;
int protocol;
ReturnStatus status;
statePtr = (DeviceState *) devicePtr->data;
interPtr = statePtr->interPtr;
dataSize = writePtr->length - net_NetworkHeaderSize[interPtr->netType];
if (devNetDebug) {
printf(
"DevNet_FsWrite: Writing packet %d:%d proto 0x%x, size %d (%d)\n",
statePtr->interPtr->netType, statePtr->interPtr->number,
statePtr->protocol, writePtr->length, dataSize);
}
if (dataSize < interPtr->minBytes ||
dataSize > interPtr->maxBytes) {
if (devNetDebug) {
printf("DevNet_FsWrite: bad dataSize %d, maxBytes %d, minBytes\n",
dataSize, interPtr->maxBytes, interPtr->minBytes);
}
return(SYS_INVALID_ARG);
}
status = ProtocolFromPacket((Address) writePtr->buffer,
interPtr->netType, &protocol);
if (status != SUCCESS) {
printf("DevNet_FsWrite: can't get protocol from packet\n");
return SYS_INVALID_ARG;
}
if (protocol != statePtr->protocol) {
if (devNetDebug) {
printf("DevNet_FsWrite: packet protocol %d != device protocol %d\n",
protocol, statePtr->protocol);
}
return(SYS_INVALID_ARG);
}
ioVector.bufAddr = (Address)((int)writePtr->buffer +
net_NetworkHeaderSize[interPtr->netType]);
ioVector.length = dataSize;
status = Net_RawOutputSync(interPtr, writePtr->buffer, &ioVector, 1);
replyPtr->length = writePtr->length;
return(status);
}
/*
*----------------------------------------------------------------------
*
* DevNet_FsClose --
*
* Close the device.
*
* Results:
* SUCCESS
*
* Side effects:
* The protocol state is marked as closed, and any packets on the
* input queue are discarded.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevNet_FsClose(devicePtr, useFlags, openCount, writerCount)
Fs_Device *devicePtr; /* Device info. */
int useFlags; /* FS_READ | FS_WRITE */
int openCount;
/* Number of times device still open. */
int writerCount; /* Number of writers still on the device. */
{
DeviceState *statePtr;
DeviceQueueState *deviceQueuePtr;
int i;
statePtr = (DeviceState *) (devicePtr->data);
deviceQueuePtr = (DeviceQueueState *) statePtr->interPtr->devNetData;
MASTER_LOCK(&deviceQueuePtr->mutex);
List_Remove((List_Links *) statePtr);
if (List_IsEmpty((List_Links *) deviceQueuePtr)) {
Net_RemovePacketHandler(statePtr->interPtr);
}
MASTER_UNLOCK(&deviceQueuePtr->mutex);
for (i=0 ; i< PACKET_QUEUE_LEN ; i++) {
free ((char *) statePtr->queue.packet[i]);
}
free((char *) statePtr);
devicePtr->data = (ClientData) NIL;
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* DevNet_FsSelect --
*
* Perform device-specific select functions on the device.
* Always indicates that the device is writable. Indicates the
* device is readable if the queue is not empty.
*
* Results:
* SUCCESS - the operation was successful.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevNet_FsSelect(devicePtr, readPtr, writePtr, exceptPtr)
Fs_Device *devicePtr;
int *readPtr;
int *writePtr;
int *exceptPtr;
{
DeviceState *statePtr;
DeviceQueueState *deviceQueuePtr;
statePtr = (DeviceState *) (devicePtr->data);
deviceQueuePtr = (DeviceQueueState *) statePtr->interPtr->devNetData;
MASTER_LOCK(&deviceQueuePtr->mutex);
if (*readPtr) {
if (QueueEmpty(statePtr->queue)) {
*readPtr = 0;
}
}
*exceptPtr = 0;
MASTER_UNLOCK(&deviceQueuePtr->mutex);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* DevNet_FsIOControl --
*
* Not implemented yet.
*
* Results:
* SUCCESS - the operation was successful.
* SYS_INVALID_ARG - bad command, or wrong buffer size.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
DevNet_FsIOControl(devicePtr, ioctlPtr, replyPtr)
Fs_Device *devicePtr;
Fs_IOCParam *ioctlPtr;
Fs_IOReply *replyPtr;
{
DeviceState *statePtr;
Net_Interface *interPtr;
ReturnStatus status = SUCCESS;
statePtr = (DeviceState *) (devicePtr->data);
interPtr = statePtr->interPtr;
/*
* Call the ioctl routine specific to the interface.
*/
status = (interPtr->ioctl)(interPtr, ioctlPtr, replyPtr);
/*
* Here should be the handling of any ioctls that are specific to
* the protocol or network devices in general.
*/
return status;
}
/*
*----------------------------------------------------------------------
*
* ProtocolFromPacket --
*
* Retrieves the protocol from a packet. For the Ethernet
* the protocol is the type field in the header. In general
* the protocol can be an arbitrary characteristic of the
* packet.
*
* Results:
* The protocol associated with the packet.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
ProtocolFromPacket(packetPtr, netType, protoPtr)
Address packetPtr; /* Network-specific packet. */
Net_NetworkType netType; /* Type of network. */
int *protoPtr; /* Place to return protocol. */
{
int protocol = 0;
ReturnStatus status = SUCCESS;
switch(netType) {
case NET_NETWORK_ETHER:
protocol = NET_ETHER_HDR_TYPE(*(Net_EtherHdr *)packetPtr);
break;
case NET_NETWORK_ULTRA:
protocol = 0;
break;
case NET_NETWORK_FDDI:
protocol = 0;
break;
default:
status = FAILURE;
}
if (status == SUCCESS) {
*protoPtr = protocol;
}
return status;
}
/*
*----------------------------------------------------------------------
*
* ProtocolFromDevice--
*
* Determine the protocol number from the protocol field in the
* device unit number. This is also a function of the network
* type.
*
* Results:
* SUCCESS if the protocol was found, FAILURE otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
ProtocolFromDevice(unitProto, interPtr, protoPtr)
int unitProto; /* Value of proto field in unit. */
Net_Interface *interPtr; /* Network interface. */
int *protoPtr; /* Place to return protocol. */
{
static int mapping[NET_NUM_NETWORK_TYPES][DEV_NET_NUM_PROTO] = {
/* NET_NETWORK_ETHER */
{0, /* Doesn't match any. */
NET_ETHER_ARP,
NET_ETHER_REVARP,
NET_ETHER_IP,
NET_ETHER_SPRITE_DEBUG,
NET_ETHER_MOP},
/* NET_NETWORK_ULTRA */
{0, /* Doesn't match any. */
0,
0,
0},
/* NET_NETWORK_FDDI */
{0, /* Doesn't match any. */
0,
0,
0,
0,
0}
};
if (unitProto < 0 || unitProto >= DEV_NET_NUM_PROTO) {
return FAILURE;
}
*protoPtr = mapping[interPtr->netType][unitProto];
return SUCCESS;
}
/*
*----------------------------------------------------------------------
*
* QueueFromInterface --
*
* This routine gets the queue structure from the interface if
* one exists, or allocates one otherwise. We need to have
* this separate routine because many devices share the same
* queue structure of an interface and we have to avoid race
* conditions when two devices are opened.
*
* Results:
* Pointer to the queue structure.
*
* Side effects:
* The queue structure is allocated.
*
*----------------------------------------------------------------------
*/
static DeviceQueueState *
QueueFromInterface(interPtr)
Net_Interface *interPtr; /* The network interface. */
{
static Sync_Lock lock = Sync_LockInitStatic("QueueFromInterface:lock");
#define LOCKPTR (&lock)
DeviceQueueState *deviceQueuePtr;
LOCK_MONITOR;
deviceQueuePtr = (DeviceQueueState *) interPtr->devNetData;
if (deviceQueuePtr == (DeviceQueueState *) NIL) {
deviceQueuePtr = (DeviceQueueState *)
malloc(sizeof(DeviceQueueState));
List_Init((List_Links *) deviceQueuePtr);
Sync_SemInitDynamic(&deviceQueuePtr->mutex,
"Dev:queueStateMutex");
interPtr->devNetData = (ClientData) deviceQueuePtr;
}
UNLOCK_MONITOR;
return deviceQueuePtr;
}